package com.onlyxiahui.common.action.description.source;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.onlyxiahui.common.action.description.util.ActionKeyUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;

/**
 * 
 * <br>
 * Date 2019-10-31 18:06:43<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class SourceBox {
	private Map<String, JavaClass> javaFileMap = new HashMap<>();
	private Map<String, Map<String, JavaMethod>> javaMethodMap = new HashMap<>();
	private Map<String, Map<String, JavaField>> javaFieldMap = new HashMap<>();

	PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
	Set<String> pathSet = new HashSet<>();

	public void load(String... paths) {
		if (null != paths) {
			for (String p : paths) {
				load(p);
			}
		}
	}

	public void load(String path) {
		boolean mark = doLoad(path);
		if (mark) {
			pathSet.add(path);
		}
	}

	public void reload() {
		for (String path : pathSet) {
			doLoad(path);
		}
	}

	/**
	 * 
	 * <br>
	 * Date 2020-04-05 13:52:03<br>
	 * 
	 * @param path
	 * @return
	 * @since 1.0.0
	 */
	private boolean doLoad(String path) {

		// classpath*:**/*.java
		// file:src/main/java/**/*.java
		boolean mark = false;
		JavaProjectBuilder builder = new JavaProjectBuilder();
//		try {
//			builder.addSource(SourceBox.class.getResource(""));
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		builder.addSourceTree(new File("src/main/java"));
		try {
			Resource[] resources = resolver.getResources(path);
			if (resources != null && resources.length > 0) {
				for (Resource resource : resources) {
					InputStream input = resource.getInputStream();
					BufferedReader reader = new BufferedReader(new InputStreamReader(input));
					try {
						builder.addSource(reader);

					} finally {
						try {
							if (null != input) {
								input.close();
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
						if (null != reader) {
							reader.close();
						}

					}
				}
				mark = true;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		Collection<JavaClass> javaClasses = builder.getClasses();
		for (JavaClass cls : javaClasses) {
			String className = cls.getFullyQualifiedName();
			javaFileMap.put(className, cls);
			put(className, cls);
		}
		return mark;
	}

	private void put(String className, JavaClass javaClass) {
		if (null != javaClass) {
			Map<String, JavaMethod> methodMap = javaMethodMap.get(className);
			if (null == methodMap) {
				methodMap = new HashMap<>(100);
				javaMethodMap.put(className, methodMap);
			}
			// TODO
//			if (className.equals("com.onlyxiahui.power.doc.controller.ApiNoteController")) {
//				System.out.println(111);
//			}
			List<JavaMethod> ms = javaClass.getMethods(true);
			if (null != ms) {
				for (JavaMethod jm : ms) {
					String key = ActionKeyUtil.getKey(jm);
					methodMap.put(key, jm);
				}
			}

			Map<String, JavaField> fieldMap = javaFieldMap.get(className);
			if (null == fieldMap) {
				fieldMap = new HashMap<>(100);
				javaFieldMap.put(className, fieldMap);
			}
			List<JavaField> fs = javaClass.getFields();
			if (null != fs) {
				for (JavaField jf : fs) {
					String name = jf.getName();
					fieldMap.put(name, jf);
				}
			}
		}
	}

	public Map<String, JavaClass> getJavaFileMap() {
		return javaFileMap;
	}

	public JavaClass getJavaClassByName(String className) {
		return javaFileMap.get(className);
	}

	public JavaMethod getJavaMethodByMethod(String className, Method m) {
		String key = ActionKeyUtil.getKey(m);
		return getJavaMethodByName(className, key);
	}

	public JavaMethod getJavaMethodByName(String className, String methodName, Parameter[] parameters) {
		String key = ActionKeyUtil.getKey(methodName, parameters);
		return getJavaMethodByName(className, key);
	}

	private JavaMethod getJavaMethodByName(String className, String methodName) {
		Map<String, JavaMethod> map = javaMethodMap.get(className);
		if (null == map) {
			return null;
		}
		return map.get(methodName);
	}

	public JavaField getJavaFieldByName(String className, String fieldName) {
		Map<String, JavaField> fieldMap = javaFieldMap.get(className);
		if (null == fieldMap) {
			return null;
		}
		return fieldMap.get(fieldName);
	}

	public void clear() {
		javaFileMap.clear();
		javaMethodMap.clear();
	}
}
